// Copyright 2007 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


package com.google.enterprise.connector.afyd;

import com.google.enterprise.connector.spi.Document;
import com.google.enterprise.connector.spi.Property;
import com.google.enterprise.connector.spi.RepositoryException;
import com.google.enterprise.connector.spi.SimpleDocument;
import com.google.enterprise.connector.spi.SimpleProperty;
import com.google.enterprise.connector.spi.SpiConstants;
import com.google.enterprise.connector.spi.Value;
import com.google.gdata.data.Category;
import com.google.gdata.data.Content;
import com.google.gdata.data.Entry;
import com.google.gdata.data.MediaContent;
import com.google.gdata.data.OtherContent;
import com.google.gdata.data.Person;
import com.google.gdata.data.TextContent;
import com.google.gdata.data.media.MediaSource;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class holds a few static methods responsible for making a Document
 * from an Entry (translating from the GData world to the Connector SPI world).
 * 
 * This class is not instantiable.
 * 
 * All of this code was taken from GdConnector.java in the open source project:
 * "google-enterprise-connector-gdata".
 * 
 * @author amsmith@google.com (Adam Smith)
 */
public class EntryDocumentizer {
  
  private static final Logger LOGGER =
    Logger.getLogger(EntryDocumentizer.class.getName());
  
  /**
   * This is a non-instantiable utility class!
   */
  private EntryDocumentizer() {}
  
  /**
   * These strings define the key names for connector-specific meta data
   * that will be packaged along with the required keys as defined in the SPI.
   */
  public static final String PROPNAME_TITLE = "title";
  public static final String PROPNAME_SUMMARY = "summary";
  public static final String PROPNAME_CATEGORY = "category";
  public static final String PROPNAME_AUTHOR = "author";
  
  /** Makes a Document from the given Entry object. */
  public static Document makeDocument (Entry entry)
  throws RepositoryException {
    
    Map map = new HashMap();
    
    // Copy required properties from entry.
    
    map.put(SpiConstants.PROPNAME_DOCID,
        makeProperty(entry.getId()));
    
    map.put(SpiConstants.PROPNAME_LASTMODIFIED,
        makeProperty(entry.getUpdated().toStringRfc822()));
        
    map.put(SpiConstants.PROPNAME_DISPLAYURL,
        makeProperty(entry.getHtmlLink().getHref()));
    
    // Build PROPNAME_MIMETYPE and PROPNAME_CONTENT from entry.
    
    Content content = entry.getContent();
    
    if (content instanceof TextContent) {
      
      TextContent textContent = (TextContent) content;
      
      map.put(SpiConstants.PROPNAME_MIMETYPE,
          makeProperty("text/html"));
      
      map.put(SpiConstants.PROPNAME_CONTENT,
          makeProperty(
              "<html><head><title>" +
              (entry.getTitle() == null ? "" : 
                entry.getTitle().getPlainText()) +
              "</title><body>" +
              textContent.getContent().getPlainText() +
              "</body></html>"));
      
    } else if (content instanceof MediaContent) {
      
      MediaContent mediaContent = (MediaContent) content;
      map.put(SpiConstants.PROPNAME_MIMETYPE,
          makeProperty(mediaContent.getMimeType().getMediaType() ));
      map.put(SpiConstants.PROPNAME_CONTENT,
          makeProperty(mediaContent.getMediaSource()));
      
    } else if (content instanceof OtherContent) {
      
      OtherContent otherContent = (OtherContent) content;
      map.put(SpiConstants.PROPNAME_MIMETYPE, 
          makeProperty(otherContent.getMimeType().getMediaType() ));
      map.put(SpiConstants.PROPNAME_CONTENT,
          makeProperty(otherContent.getBytes() ));
      
    } else {
      if (LOGGER.isLoggable(Level.SEVERE))
        LOGGER.severe("Unhandled content: " + content);
      throw new RepositoryException();
    }
    
    // Extract additional properties to be sent as meta data.
    // (not defined by SpiConstants)

    if (entry.getTitle() != null)
      map.put(PROPNAME_TITLE,
          makeProperty(entry.getTitle().getPlainText()));
    
    if (entry.getSummary() != null)
      map.put(PROPNAME_SUMMARY,
          makeProperty(entry.getSummary().getPlainText()));
   
    // these are strings like "http://schemas.google.com/g/2005#event"
    if (entry.getCategories() != null) {
      List categoryList = new LinkedList();
      for (Iterator ci = entry.getCategories().iterator(); ci.hasNext(); ) {
        Category category = (Category) ci.next();
        categoryList.add( Value.getStringValue(category.getTerm()) );
      }
      map.put(PROPNAME_CATEGORY, new SimpleProperty(categoryList));
    }
    
    if (entry.getAuthors() != null) {
      List authorList = new LinkedList();
      for (Iterator ai = entry.getAuthors().iterator(); ai.hasNext(); ) {
        Person person = (Person) ai.next();
        authorList.add(Value.getStringValue(person.getName()) );
        if (person.getEmail() != null) {
          authorList.add(Value.getStringValue( person.getEmail()) );
        }
      }
      map.put(PROPNAME_AUTHOR, new SimpleProperty(authorList));
    }

    return new SimpleDocument(map);
  }

  /** Make a SimpleProperty with one value using the given string. */
  public static Property makeProperty(String str) {
    List strList = new LinkedList();
    strList.add(Value.getStringValue(str));
    return new SimpleProperty(strList);
  }
  
  /**
   * Make a SimpleProperty with one value using the bytes read from the given
   * MediaSource.
   */
  public static Property makeProperty(MediaSource source) 
    throws RepositoryException {
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    try {
      MediaSource.Output.writeTo(source, stream);
    } catch (IOException ioe) {
      throw new RepositoryException(ioe);
    }
    List list = new LinkedList();
    list.add(Value.getBinaryValue(stream.toByteArray()));
    return new SimpleProperty(list);
  }
  
  /**
   * Make a SimpleProperty with one value using the given byte array directly.
   */
  public static Property makeProperty(byte [] bytes) {
    List list = new LinkedList();
    list.add(Value.getBinaryValue(bytes));
    return new SimpleProperty(list);
  }
}
